#include <iostream>
#include <mysql/mysql.h>
#include <queue>
#include <mutex>
#include <memory>
#include "StdMysql.h"
//一个线程中被访问
//锁
struct stdSqlPrivate
{
    MYSQL mysql;
    bool isConnected;//是否连接
};

stdMysql::stdMysql(): p(new stdSqlPrivate)
{
    mysql_init(&p->mysql);
    p->isConnected = false;
}

stdMysql::~stdMysql()
{
    if(p->isConnected == false)
        mysql_close(&p->mysql);
    delete p;
}

bool stdMysql::Connect(CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, int port)
{
    if(mysql_real_connect(&p->mysql, host.c_str(), user.c_str(), passwd.c_str(), databaseName.c_str(), port, nullptr, 0) == nullptr)
    {
        std::cout<<"数据库连接失败"<<mysql_error(&p->mysql);
        return false;
    }
    p->isConnected = true;
    return true;
}

bool stdMysql::isConnected()
{
    if(p->isConnected == false)
    {
        return false;
    }
    if(mysql_ping(&p->mysql) != 0)
    {
        std::cout<<"msql连接已断开"<<std::endl;
    }
   return true;
}

bool stdMysql::Execute(CStrRef sql)
{
    if(mysql_real_query(&p->mysql, sql.c_str(), sql.size()) != 0)
    {
        std::cout<<"语句执行失败"<<mysql_error(&p->mysql)<<std::endl;
        return false;
    }
    return true;
}

std::vector<StrArray> stdMysql::GetTableInfo(CStrRef sql)
{
    if(Execute(sql) == false)
    {

    }
    MYSQL_RES *result = mysql_use_result(&p->mysql);
    if(result == nullptr)
    {
        std::cout<<"获取结果集失败"<<std::endl;
    }
    //获取列数
    int filedcount = mysql_field_count(&p->mysql);
    
    std::vector<StrArray> resultArray;
  //从结果集中按行缺数据
    while(MYSQL_ROW sqlRow  = mysql_fetch_row(result))
    {
        StrArray array;
        for(int i = 0; i <filedcount; i++)
        {
            array.push_back(sqlRow[i]);
        }
        resultArray.push_back(std::move(array));
    }
    //释放结果集
    mysql_free_result(result);
    return resultArray;
}

bool stdMysql::ExecuteTransaction(const StrArray& sqlVec)
{

    if(mysql_autocommit(&p->mysql, false)!= 0)
    {
        std::cout<<"启动手动事务失败\n";
        return false;
    }
    for(auto &sql : sqlVec)
    {
        if(Execute(sql) == false)
        {
            mysql_rollback(&p->mysql);
            return false;
        }
    }
    mysql_commit(&p->mysql);
    mysql_autocommit(&p->mysql, true);
    return true;
}


struct StdMysqlPoolPrivate
{
    void SetParams(int MaxCount, CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, unsigned int port)
    {
        this->host = host;
        this->user  =user;
        this->passwd = passwd;
        this->databaseName = databaseName;
        this->port = port;
        this->MaxCount = MaxCount;
    }
    std::string host;
    std::string user;
    std::string passwd;
    std::string databaseName;
    unsigned int port;
    std::queue<MysqlPtr> MysqlQueue;//连接队列
    std::mutex poolMutex;//连接池锁
    int MaxCount;//最大连接数量
};
StdMysqlPool::StdMysqlPool():p(new StdMysqlPoolPrivate)
{

}

StdMysqlPool *StdMysqlPool::GlobalInstance()
{
    static StdMysqlPool pool;
    return &pool;;
}

StdMysqlPool::~StdMysqlPool()
{
    while(p->MysqlQueue.empty() == false)
    {
        p->MysqlQueue.pop();
    }
    delete p;
}

bool StdMysqlPool::SetParams(int MaxCount, CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, unsigned int port)
{
    p->SetParams(MaxCount, host, user, passwd, databaseName, port);
    for(int i = 0; i <MaxCount; i++)
    {
        MysqlPtr ptr = std::make_shared<stdMysql>();
        if(ptr->Connect(host, user, passwd, databaseName, port) == false)
        {
            return false;
        }
        p->MysqlQueue.push(ptr);
    }
    return true;
}

//mysql连接Tcp 当网络不稳定会导致连接自动断开
MysqlPtr StdMysqlPool::GetMysql()
{
    //保证线程安全
    std::lock_guard<std::mutex> lock(p->poolMutex);
    while(p->MysqlQueue.empty() == false)
    {
        //对头指针处于断开状态
        if(p->MysqlQueue.front()->isConnected() == false)
        {
            p->MysqlQueue.pop();
        }
        //队头指针可用
        else
        {   
            auto ptr = p->MysqlQueue.front();
            p->MysqlQueue.pop();
            return ptr;
        }
    }

    MysqlPtr ptr = std::make_shared<stdMysql>();
    if(ptr->Connect(p->host, p->user, p->passwd, p->databaseName, p->port) == false)
    {
        std::cout<<"数据库连接失败";
        return nullptr;
    }
    return MysqlPtr();
}

void StdMysqlPool::CloseMysql(MysqlPtr ptr)
{
    //指针已经断开连接
    if(ptr->isConnected() == false)
        return;
    std::lock_guard<std::mutex> lock(p->poolMutex);
    p->MysqlQueue.push(ptr);

}
